package main

import (
	"fmt"
	"io"
	"net/http"
	"net/rpc"
	"net/rpc/jsonrpc"

	"gitlab.com/go-course-project/go15/skills/rpc/service"
)

// func fn(request, *response) error
// &HelloService{} 对象
// var _ service.Service = &HelloService{}
// *HelloService nil ==  int(6)
var _ service.Service = (*HelloServiceServer)(nil)

type HelloServiceServer struct {
}

// RPC 函数签名
func (s *HelloServiceServer) Hello(request string, response *string) error {
	// request: tom
	// resonse: hello, tom
	*response = fmt.Sprintf("hello, %s", request)

	// var a string
	// a = "xxxx"
	return nil
}

// HelloService.Hello 暴露为一个RPC
// 把 HelloService这个对象给 rpc框架, rpc框架会把这个对象上的方法暴露到网络中
type RPCReadWriteCloser struct {
	io.Writer
	io.ReadCloser
}

func NewRPCReadWriteCloserFromHTTP(w http.ResponseWriter, r *http.Request) io.ReadWriteCloser {
	return RPCReadWriteCloser{w, r.Body}
}

func main() {
	// 注册对象到rpc框架
	rpc.RegisterName("HelloService", &HelloServiceServer{})

	// 启动rpc服务
	// 然后我们建立一个唯一的TCP链接，
	// listener, err := net.Listen("tcp", ":1234")
	// if err != nil {
	// 	log.Fatal("ListenTCP error:", err)
	// }

	// 通过rpc.ServeConn函数在该TCP链接上为对方提供RPC服务。
	// 没Accept一个请求，就创建一个goroutie进行处理
	// for {
	// 	conn, err := listener.Accept()
	// 	if err != nil {
	// 		log.Fatal("Accept error:", err)
	// 	}

	// 	// 前面都是tcp的知识, 到这个RPC就接管了
	// 	// 因此 你可以认为 rpc 帮我们封装消息到函数调用的这个逻辑,
	// 	// 提升了工作效率, 逻辑比较简洁，可以看看他代码
	// 	// go handler(conn)
	// 	// 采用JSON 格式的编码解码器
	// 	go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
	// }

	// RPC的服务架设在“/jsonrpc”路径，
	// 在处理函数中基于http.ResponseWriter和http.Request类型的参数构造一个io.ReadWriteCloser类型的conn通道。
	// 然后基于conn构建针对服务端的json编码解码器。
	// 最后通过rpc.ServeRequest函数为每次请求处理一次RPC方法调用
	// w http.ResponseWriter, r *http.Request --> io.ReadWriteCloser
	// type ReadWriteCloser interface {
	// 	Reader
	// 	Writer
	// 	Closer
	// }
	http.HandleFunc("/jsonrpc", func(w http.ResponseWriter, r *http.Request) {
		conn := NewRPCReadWriteCloserFromHTTP(w, r)
		rpc.ServeRequest(jsonrpc.NewServerCodec(conn))
	})

	http.ListenAndServe(":1234", nil)
}
